<?php

namespace app\index\service;

use app\common\exception\ConsultingQuestionNumNotEnoughException;
use app\common\model\Consulting;
use app\common\model\ConsultingMessage;
use app\common\model\ConsultingServer;
use app\common\model\Member;
use app\common\service\MiniProgramMessageService;
use app\index\dao\ConsultingMessageDao;
use app\index\model\UM as UM;
use app\index\traits\ChatFreelyTrait;
use app\index\traits\MessagePropertyTrait;
use app\index\traits\MutedTrait;
use ShidianUtil\Utils\PregUtil;
use think\Collection;
use think\exception\ValidateException;

/**
 * Class MessageService
 * @package app\consulting\service
 */
class MessageService
{
    use MutedTrait;
    use ChatFreelyTrait;
    use MessagePropertyTrait;

    // dao
    private ConsultingMessageDao $consultingMessageDao;
    private MessageContentService $contentService;

    public function __construct(ConsultingMessageDao $consultingMessageDao, MessageContentService $contentService)
    {
        $this->consultingMessageDao = $consultingMessageDao;
        $this->contentService = $contentService;
    }

    /**
     * 列表
     * @param UM $user
     * @param int|null $page
     * @param int|null $page_size
     * @return Collection
     */
    public function index(UM $user, $consultingId, $firstId, ?int $page = null, ?int $page_size = null): Collection
    {
        $memberId = $user->id;

        if (!is_id($consultingId)) {
            return \think\model\Collection::make();
        }

        $collection = $this->consultingMessageDao->selectConsultingHistory($consultingId, $memberId, $firstId, $page, $page_size);

        // 前端要倒序的
        $collection = $collection->reverse();

        $this->getMessageContentService()->wrapDynamicMessages($collection);

        // is_my
        $collection->each(function (ConsultingMessage $record) use ($user) {
            $record->isMy = $this->getIsMy($record, $user->id);
        });

        return $collection;
    }

    /**
     * 消息详情
     * @param UM $user
     * @param $id
     * @return ConsultingMessage|null
     */
    public function read(UM $user, $id): ?ConsultingMessage
    {
        $record = $this->consultingMessageDao->findConsultingMessage($id);

        $this->getMessageContentService()->wrapDynamicMessage($record);

        $record->isMy = $this->getIsMy($record, $user->id);

        return $record;
    }

    /**
     * 发消息（用户 + 律师）
     * @param UM $user
     * @param array $params
     */
    public function add(UM $user, array $params): void
    {

        $consultingId = $params['consultingId'] ?? null;
        $messageType = $params['messageType'] ?? null;
        $content = $params['content'] ?? null;

        if (!is_id($consultingId)) {
            throw new ValidateException('请选择一个咨询');
        }
        if (is_blank($messageType)) {
            throw new ValidateException('请选择一个正确的消息类型');
        }
        if (is_null($content)) {
            throw new ValidateException('请输入消息内容');
        }

        // 检查消息参数格式
        $this->checkFormatContent($messageType, $content);

        // 用户是否被禁言
        $this->checkMuted($user);

        // 咨询
        $consulting = Consulting::where('id', $consultingId)->find();
        if (is_null($consulting)) {
            throw new ValidateException('该咨询不存在或已被删除');
        }
        // 是否已结束
        if ($consulting->isEnd == 1) {
            throw new ValidateException('该咨询已完成，如需咨询请重新发起');
        }

        // 用户
        if ($user->type == 0) {

            if ($consulting->isAsked == '0') {
                // 首句提问，首句提问必须是文本消息

                if ($messageType != ConsultingMessage::TEXT) {
                    throw new ValidateException('首句提问必须是文字消息');
                }
                if (mb_strlen($content['text']) < 8) {
                    throw new ValidateException('提问不能少于8个字');
                }
            } else {
                // 追问

                // 是否还有剩余追问次数
                $isChatFreely = $this->isChatFreely($consulting);
                if (!$isChatFreely && $consulting->questionsNum <= 0) {

                    // 触发剩余次数不足
                    event('ConsultingQuestionNumNotEnough', $consulting);

                    throw new ConsultingQuestionNumNotEnoughException();
                }
            }
        }

        // 律师
        if ($user->type == 1) {

            if ($consulting->lawyerId != $user->id) {
                throw new ValidateException('您不能回答该咨询，因为您不是该咨询匹配的律师');
            }

            // 首句回复
            if ($consulting->isAnswered != 1) {

                if ($messageType == ConsultingMessage::TEXT) {
                    if (mb_strlen($content['text']) < 15) {
                        throw new ValidateException('首句回复是引导客户付费的基础，建议大于15字');
                    }
                }
            }
        }

        // 过滤消息内容
        $replaceTotal = $this->checkContent($user, $consulting, $messageType, $content);

        // 发消息
        $message = $this->getMessageSendService()->sendMemberMessage($consulting, $user->type, $user->id, $messageType, $content);

        // 触发消息创建事件
        if ($consulting->businessType == Consulting::BT_RANDOM) {
            // 如果是极速咨询

            event('RandomConsultingMessageCreated', ['user' => $user, 'message' => $message, 'consulting' => $consulting]);
        } elseif ($consulting->businessType == Consulting::BT_DESIGNATED) {
            // 如果是找律师咨询

            event('DesignateConsultingMessageCreated', ['user' => $user, 'message' => $message, 'consulting' => $consulting]);
        } else {
            // 如果是V1版本咨询

            event('ConsultingMemberMessageCreated', ['user' => $user, 'message' => $message, 'consultingId' => $consultingId]);
        }

        // 发生了替换
        if ($replaceTotal > 0) {
            // 发消息
            if (is_id($consulting->memberId)) {
                $content0 = [
                    'templates' => [
                        [
                            'type' => 'text', 'red' => '102', 'text' => '平台暂不支持直接发送手机号，微信号等联系方式，请使用顶部相关功能进行操作',
                        ]
                    ],
                ];
                $this->getMessageSendService()->sendSystemMessage($consulting, $consulting->memberId, ConsultingMessage::SYSTEM, $content0);
            }
            if (is_id($consulting->lawyerId)) {
                $content0 = [
                    'templates' => [
                        [
                            'type' => 'text', 'red' => '102', 'text' => '平台暂不支持直接发送手机号，微信号等联系方式，请引导用户使用相关付费功能',
                        ]
                    ],
                ];
                $this->getMessageSendService()->sendSystemMessage($consulting, $consulting->lawyerId, ConsultingMessage::SYSTEM, $content0);
            }
        }
    }

    /**
     * 发服务卡片消息
     * @param UM $user
     * @param array $params
     * @return void
     */
    public function addServerMessage(UM $user, array $params): void
    {

        $consultingId = $params['consultingId'] ?? null;
        $action = $params['action'] ?? 'custom';
        $messageType = ConsultingMessage::SERVER;
        $content = $params['content'] ?? null;

        if (!is_id($consultingId)) {
            throw new ValidateException('请选择一个咨询');
        }

        // 如果点击的是电话咨询或者畅聊按钮
        if ($action == 'phone') {

            $server = ConsultingServer::where('serverType', ConsultingServer::PHONE)->find();
            if (!$server) {
                throw new ValidateException('暂不支持该服务');
            }
            $content = [
                'id' => $server->id
            ];
        } elseif ($action == 'chatFreely') {

            $tableName = ConsultingServer::getTable();
            $server = ConsultingServer::leftJoin('consulting_server cs', "$tableName.id=cs.recommendServerId")
                ->where('cs.serverType', ConsultingServer::FREE)
                ->tableField(true, $tableName)
                ->find();
            if (!$server) {
                throw new ValidateException('暂不支持该服务');
            }
            $content = [
                'id' => $server->id
            ];
        }

        if (is_null($content)) {
            throw new ValidateException('请输入消息内容');
        }

        // 检查消息参数格式
        $this->checkFormatContent($messageType, $content);

        // 用户是否被禁言
        $this->checkMuted($user);

        // 咨询
        $consulting = Consulting::where('id', $consultingId)->find();
        if (is_null($consulting)) {
            throw new ValidateException('该咨询不存在或已被删除');
        }

        // 检查所选服务是否正确
        $server = ConsultingServer::where('id', $content['id'])->where('serverType', '<>', ConsultingServer::FREE)->find();
        if (!$server) {
            throw new ValidateException('该服务不存在，或已被删除');
        }

        $contentId = $content['id'];

        // 检查该咨询最近X条消息内是否包含同类型卡片
        $repeat = ConsultingMessage::where('consultingId', $consultingId)
            ->where('messageType', ConsultingMessage::SERVER)
            ->whereRaw("json_extract( `content`, '$.id' ) = $contentId")
            ->find();
        if ($repeat) {
            throw new ValidateException('卡片已发送');
        }

        // 发消息
        $contentParams = [
            'id' => $contentId,
        ];
        $message = $this->getMessageSendService()->sendSystemMessage($consulting, 0, $messageType, $contentParams);


    }

    /**
     * 发评价消息
     * @param UM $user
     * @param array $params
     * @return void
     */
    public function addCommentConsultingMessage(UM $user, array $params): void
    {

        $consultingId = $params['consultingId'] ?? null;
        $messageType = ConsultingMessage::COMMENT;

        if (!is_id($consultingId)) {
            throw new ValidateException('请选择一个咨询');
        }

        // 用户是否被禁言
        $this->checkMuted($user);

        // 咨询
        $consulting = Consulting::where('id', $consultingId)->find();
        if (is_null($consulting)) {
            throw new ValidateException('该咨询不存在或已被删除');
        }

        // 检查该咨询最近X条消息内是否包含同类型卡片
        $repeat = ConsultingMessage::where('consultingId', $consultingId)
            ->where('messageType', $messageType)
            ->find();
        if ($repeat) {
            throw new ValidateException('卡片已发送');
        }

        // 发评价消息给用户
        $content = [
            'consultingId' => $consulting->id,
            'lawyerId' => $consulting->lawyerId,
            'level' => null,
        ];
        $this->getMessageSendService()->sendSystemMessage($consulting, 0, $messageType, $content);

        // 发小程序订阅消息给用户
        $lawyerName = Member::where('id', $consulting->lawyerId)->value('realname') . '律师';
        $this->getMiniProgramMessageService()->messageRemindComment($consulting->memberId, $consulting->ask, $lawyerName);
    }

    /**
     * 检查消息格式
     * @param $messageType
     * @param $content
     * @return void
     */
    private function checkFormatContent($messageType, &$content): void
    {
        if (!is_array($content)) {
            throw new ValidateException('消息内容格式不正确');
        }

        switch ($messageType) {
            case ConsultingMessage::TEXT:
                $content['text'] = $content['text'] ?? null;
                if (is_blank($content['text'])) {
                    throw new ValidateException('请输入消息内容');
                }

                // 文字长度
                if (mb_strlen($content['text']) > 2000) {
                    throw new ValidateException('消息内容过长');
                }

                break;
            case ConsultingMessage::IMAGE:
                if (is_blank($content['url'] ?? null)) {
                    throw new ValidateException('请上传图片');
                }

                $content['width'] = $content['width'] ?? null;
                if (!is_numeric($content['width']) || $content['width'] <= 0) {
                    throw new ValidateException('图片宽度不正确');
                }

                $content['height'] = $content['height'] ?? null;
                if (!is_numeric($content['height']) || $content['height'] <= 0) {
                    throw new ValidateException('图片高度不正确');
                }

                break;
            case ConsultingMessage::SERVER:
                if (!is_id($content['id'] ?? null)) {
                    throw new ValidateException('请选择一个服务');
                }

                break;
            case 'file':
                if (is_blank($content['url'] ?? null)) {
                    throw new ValidateException('请上传文件');
                }

                // 设置默认文件名
                if (is_blank($content['name'] ?? null)) {
                    $content['name'] = basename($content['url']);
                }

                break;
            case 'audio':
                if (is_blank($content['url'] ?? null)) {
                    throw new ValidateException('请上传音频');
                }

                // 设置默认文件名
                if (is_blank($content['name'] ?? null)) {
                    $content['name'] = basename($content['url']);
                }

                break;
            case 'media':
                if (is_blank($content['url'] ?? null)) {
                    throw new ValidateException('请上传视频文件');
                }

                // 设置默认文件名
                if (is_blank($content['name'] ?? null)) {
                    $content['name'] = basename($content['url']);
                }

                break;
            default:
                throw new ValidateException('消息格式不正确');
        }
    }

    /**
     * 检查消息内容
     * @param $user
     * @param $consulting
     * @param $messageType
     * @param $content
     * @return void
     */
    private function checkContent($user, $consulting, $messageType, &$content)
    {
        // 不允许发电话号码,微信，QQ等
        switch ($messageType) {
            case ConsultingMessage::TEXT:

                $text = $content['text'];

                // 防止xss攻击
                $text = htmlspecialchars(strip_tags($text));

                // 是否包含手机号
                $phone_pattern = '/';
                $phone_pattern .= '(1|一|one){1}';
                $phone_pattern .= '(0|1|2|3|4|5|6|7|8|9|零|一|二|三|四|五|六|七|八|九|壹|贰|叁|肆|伍|陆|柒|捌|玖|zero|one|two|three|four|five|six|seven|eight|nine| |\!|\@|\#|\$|\%|\^|\&|\*|\(|\)|-|_|=|\+|\{|\}|\[|\]|\:|\;|\"|\'|\,|\.|\<|\>|\?|\/|\`|\~|\/|\\\|\|){9,}';
                $phone_pattern .= '(0|1|2|3|4|5|6|7|8|9|零|一|二|三|四|五|六|七|八|九|壹|贰|叁|肆|伍|陆|柒|捌|玖|zero|one|two|three|four|five|six|seven|eight|nine){1}';
                $phone_pattern .= '/i';

                // 微信号
                // $wechat_pattern = '/[a-zA-Z]{1}[-_a-zA-Z0-9]{5,19}+/isu';
                $wechat_pattern = '/(微|v)[[:punct:]|\s]*(信|x)/is';

                // QQ号
                $qq_pattern = '/[q]{1,2}.*[1-9]\d{4,}/isu';

                // 正则表达式数组
                $pattern_arr = [
                    $phone_pattern,
                    $wechat_pattern,
                    $qq_pattern
                ];

                // 总替换次数
                $total = 0;

                // 依次替换
                $pregUtil = $this->getPregUtil();
                foreach ($pattern_arr as $pattern) {
                    $text = $pregUtil->replace($pattern, $text, '***', -1, $count);
                    $total += $count;
                }

                $content['text'] = $text;

                return $total;
                break;
        }

        return 0;
    }

    private function getMessageSendService(): MessageSendService
    {
        return app(MessageSendService::class);
    }

    private function getMessageContentService(): MessageContentService
    {
        return app(MessageContentService::class);
    }

    private function getPregUtil(): PregUtil
    {
        return app(PregUtil::class);
    }

    private function getMiniProgramMessageService(): MiniProgramMessageService
    {
        return app(MiniProgramMessageService::class);
    }
}
